package uk.ac.cam.echo.reader;

import java.util.HashSet;
import java.util.Set;

/**
 * @author Andrius Dagys
 */

public class RFID1 {

	public static final int BAUDRATE = 115200;
	//
	// Commands
	//
	public static final String PKT_PING = "0108000304FF0000";
	public static final String PKT_AGCTGL = "0109000304F0000000";
	public static final String PKT_AMPMTGL = "0109000304F1FF0000";
	public static final String PKT_PASSBEEP = "010900030419F00000";
	public static final String PKT_FAILBEEP = "010900030420F00000";
	public static final String PKT_15693INVRQ = "010B000304140601000000";
	public static final String PKT_TISIDPOLL = "010B000304340050000000";
	public static final String PKT_MODEMEM = "010C00030410002101000000";
	public static final String PKT_MODEUID = "010C00030410002101020000";
	public static final String PKT_TAGIT = "010C00030410002101130000";
	
	public static final char EOP_CHAR = '>';
	//
	// Provides serial port abstraction
	//
	private Communicator comm;
	//
	// Hold a list of tags currently read
	//
	private Set<String> inventory;

	public RFID1(String portName) {
		comm = new Communicator(portName, RFID1.BAUDRATE);
		comm.setPacketFormat(RFID1.EOP_CHAR);		
		inventory = new HashSet<String>(); 		
	}
	
	public Set<String> poll() throws StateUnchangedException {
		String ignore, response;
		//
		// Send required commands to poll for tags
		//
		comm.send(RFID1.PKT_MODEUID);
		ignore = comm.get();
		comm.send(RFID1.PKT_AGCTGL);
		ignore = comm.get();
		comm.send(RFID1.PKT_AMPMTGL);
		ignore = comm.get();
		comm.send(RFID1.PKT_15693INVRQ);
		response = comm.get();
		
		//
		// Get a set of new tags currently read
		//
		Set<String> newTags = new HashSet<String>();
		Set<String> currentTags = new HashSet<String>();
		
		String[] temp = response.split("\n");
		for (int i = 2; i < temp.length - 2; i++) {
			String tagId = temp[i].substring(1, temp[i].length() - 1).split(",")[0];
			//
			// Need to reverse the byte order, i.e. the tag string
			//
			tagId = reverseByteOrder(tagId);
			currentTags.add(tagId);
			if (valid(tagId) && !inventory.contains(tagId)) {
				newTags.add(tagId);
			}
		}
		inventory = currentTags;
		
		if (newTags.isEmpty()) throw new StateUnchangedException();
		comm.send(PKT_PASSBEEP);
		return newTags;
	}
	/**
	 * Reverses the byte order of the tag.
	 * In this case, the input is a hex string, meaning that
	 * two consecutive characters will be treated as a single byte 
	 * 
	 * @param s A hex string to reverse the byte order for
	 * @return
	 */
	private String reverseByteOrder(String s) {
		byte[] buff = s.getBytes();
		byte[] rbuff = new byte[buff.length];
		for (int k = 0, i = buff.length - 1; i > 0; i -= 2){
			rbuff[k++] = buff[i - 1];
			rbuff[k++] = buff[i];
		}
		return new String(rbuff);
	}

	private boolean valid(String tag){
		return tag.length() > 5;
	}
	
	public void passBeep(){
		comm.send(PKT_PASSBEEP);
		comm.get();
	}
	
	public void failBeep(){
		comm.send(PKT_FAILBEEP);
		comm.get();
	}
	
	public void stop() {
		comm.closePort();
	}
}
